#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <GL/glx.h>
#include <GL/glu.h>
#include <sys/time.h>
#include <time.h>
#include "gplib.h"


//bReorder = 0;
//g_nByteOffset[] = { 3, 1, -1, -3 };
//g_nMaxPairsOfCoord = 24;


Display                 *dpy;
Window                  root;
GLint                   att[] = { GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None };
XVisualInfo             *vi;
Colormap                cmap;
XSetWindowAttributes    swa;
Window                  win;
GLXContext              glc;
XWindowAttributes       gwa;
XEvent                  xev;

#define WIDTH  1024
#define HEIGHT 768

GLuint texture[1];
char data[WIDTH * HEIGHT * 3];
TGraphicContext ctx;

inline void Test_SetPixel(int nCnt) {
	TColor c = { 0xFF, 0xFF, 0xFF };

	for (; nCnt > 0; nCnt--) {
		SetPixel(&ctx, 50, 50, c);
	}
}

inline void Test_LineTo(int nCnt) {
	TColor c = { 0xFF, 0xFF, 0xFF };

	for (; nCnt > 0; nCnt--) {
		MoveTo(&ctx, 50, 50);
		LineTo(&ctx, 690, 530, c);
	}
}

inline void Test_XorLineTo(int nCnt) {
	TColor c = { 0xFF, 0xFF, 0xFF };

	for (; nCnt > 0; nCnt--) {
		MoveTo(&ctx, 50, 50);
		XorLineTo(&ctx, 690, 530, c);
	}
}

inline void Test_ThickLineTo(int nCnt) {
	TColor c = { 0xFF, 0xFF, 0xFF };

	for (; nCnt > 0; nCnt--) {
		MoveTo(&ctx, 50, 50);
		ThickLineTo(&ctx, 690, 530, c);
	}
}

inline void Test_QuickThickLineTo(int nCnt) {
	TColor c = { 0xFF, 0xFF, 0xFF };

	for (; nCnt > 0; nCnt--) {
		MoveTo(&ctx, 50, 50);
		QuickThickLineTo(&ctx, 690, 530, c);
	}
}

inline void Test_MaskLineTo(int nCnt) {
	TColor c = { 0xFF, 0xFF, 0xFF };

	for (; nCnt > 0; nCnt--) {
		MoveTo(&ctx, 50, 50);
		MaskLineTo(&ctx, 690, 530, c);
	}
}

inline void Test_DrawPoly(int nCnt, int nCount, int *pnPoints) {
	TColor c1 = { 0xE0, 0xFF, 0xFF };
	TColor c2 = { 0xFF, 0xFF, 0xFF };

	for (; nCnt > 0; nCnt--) {
		DrawPoly(&ctx, nCount, pnPoints, c1, c2);
	}
}

inline void Test_Rectangle(int nCnt) {
	TColor c = { 0xFF, 0xFF, 0xFF };

	for (; nCnt > 0; nCnt--) {
		Rectangle(&ctx, 50, 50, 300, 200, c);
	}
}

inline void Test_FillRectangle(int nCnt) {
	TColor c = { 0xFF, 0xFF, 0xFF };

	for (; nCnt > 0; nCnt--) {
		FillRectangle(&ctx, 50, 50, 300, 200, c);
	}
}

inline void Test_DrawMaskRect(int nCnt) {
	TColor c = { 0xFF, 0xFF, 0xFF };

	for (; nCnt > 0; nCnt--) {
		DrawMaskRect(&ctx, 300, 200, 50, 50, c);
	}
}

inline void Test_DrawReliefRect(int nCnt, char bSunken) {
	TColor c1 = { 0xFF, 0xFF, 0xFF };
	TColor c2 = { 0x25, 0x25, 0x25 };

	for (; nCnt > 0; nCnt--) {
		DrawReliefRect(&ctx, 50, 50, 300, 200, c1, c2, bSunken);
	}
}


void LoadGLTexture() {
	struct timeval start_time, end_time;
	int i, j, x, y;
	unsigned char color;
	int pnPoints3[(3 + 2) * 2] = {
		  50,  0,
		  100, 100,
		  0,   100
	};
	int pnPoints4[(4 + 2) * 2] = {
		  0,   0,
		  100, 0,
		  100, 100,
		  0,   100
	};
	int pnPoints5[(5 + 2) * 2] = {
		  50,  0,
		  100, 50,
		  75,  100,
		  25,  100,
		  0,   50
	};
	int pnPoints6[(6 + 2) * 2] = {
		  50,  0,
		  100, 25,
		  100, 75,
		  50,  100,
		  0,   75,
		  0,   25
	};
	int pnPoints7[(7 + 2) * 2] = {
		  50,  0,
		  100, 25,
		  100, 75,
		  75,  100,
		  25,  100,
		  0,   75,
		  0,   25
	};
	int pnPoints8[(8 + 2) * 2] = {
		  25,  0,
		  75,  0,
		  100, 25,
		  100, 75,
		  75,  100,
		  25,  100,
		  0,   75,
		  0,   25
	};
	int nCount = 8;
	int *pnPoints = pnPoints8;
	int pnPnt[(24 + 2) * 2];
	static char bSunken;
	TColor c;
	TColor c1 = { 0xFF, 0xFF, 0xFF };
	TColor c2 = { 0xFF, 0x0, 0x0 };

	SetLineMask(&ctx, 0xFF0000FF, 32);
	SetThickness(&ctx, 20);
	bSunken = !bSunken;

/*	MoveTo(&ctx, 20, 20);
	ThickLineTo(&ctx, 100, 20, 255);
	MoveTo(&ctx, 20, 20);
	LineTo(&ctx, 100, 20, 224); */

	gettimeofday(&start_time, NULL);

//	Test_DrawPoly(10001, nCount, pnPoints);
//	Test_DrawReliefRect(10001, bSunken);
//	Test_LineTo(10001);
//	Test_Rectangle(10001);

/*	for (i = 0; i < 1000; i++) {
		x = rand() % (WIDTH - 100);
		y = rand() % (HEIGHT - 100);

		for (j = 0; j < nCount * 2; j += 2) {
			pnPnt[j    ] = pnPoints[j    ] + x;
			pnPnt[j + 1] = pnPoints[j + 1] + y;
		}

		c1.b1 = rand() % 256;
		c1.b2 = rand() % 256;
		c1.b3 = rand() % 256;
		DrawPoly(&ctx, nCount, pnPnt, c2, c1);
	} */

	for (i = 0; i < 1; i++) {
		c.b1 = rand() % 0xFF;
		c.b2 = rand() % 0xFF;
		c.b3 = rand() % 0xFF;
		MoveTo(&ctx, rand() % WIDTH, rand() % HEIGHT);
		MaskLineTo(&ctx, rand() % WIDTH, rand() % HEIGHT, c);
	}

	gettimeofday(&end_time, NULL);
	if (end_time.tv_sec > start_time.tv_sec) {
		end_time.tv_usec += 1000000;
	}
	printf("%d µs\n", (int)(end_time.tv_usec - start_time.tv_usec));
}

void DrawScene() {
	glClearColor(1., 1., 1., 1.);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0., 1., 1., 0., 1., 20.);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(0., 0., 10., 0., 0., 0., 0., 1., 0.);

	LoadGLTexture();
	glBindTexture(GL_TEXTURE_2D, texture[0]);

	switch (ctx.nBitsPerPixel) {
	case 8:
		glTexImage2D(GL_TEXTURE_2D, 0, 3, WIDTH, HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE_3_3_2, data);
		break;
	case 16:
		glTexImage2D(GL_TEXTURE_2D, 0, 3, WIDTH, HEIGHT, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data);
		break;
	case 24:
		glTexImage2D(GL_TEXTURE_2D, 0, 3, WIDTH, HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
		break;
	}
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	glBegin(GL_QUADS);
		glTexCoord2f(0., 0.); glVertex3f(0., 0., 0.);
		glTexCoord2f(1., 0.); glVertex3f(1., 0., 0.);
		glTexCoord2f(1., 1.); glVertex3f(1., 1., 0.);
		glTexCoord2f(0., 1.); glVertex3f(0., 1., 0.);
	glEnd();
}

int main(int argc, char *argv[]) {
	dpy = XOpenDisplay(NULL);

	if (dpy == NULL) {
		printf("\n\tcannot connect to X server\n\n");
		exit(0);
	}

	root = DefaultRootWindow(dpy);

	vi = glXChooseVisual(dpy, 0, att);

	if (vi == NULL) {
		printf("\n\tno appropriate visual found\n\n");
		exit(0);
	} else {
		printf("\n\tvisual %p selected\n", (void *)vi->visualid);
	}/* %p creates hexadecimal output like in glxinfo */

	cmap = XCreateColormap(dpy, root, vi->visual, AllocNone);

	swa.colormap = cmap;
	swa.event_mask = ExposureMask | KeyPressMask | ButtonPressMask;

	win = XCreateWindow(dpy, root, 0, 0, WIDTH, HEIGHT, 0, vi->depth, InputOutput, vi->visual, CWColormap | CWEventMask, &swa);

	XMapWindow(dpy, win);
	XStoreName(dpy, win, "GPLib");

	glc = glXCreateContext(dpy, vi, NULL, GL_TRUE);
	glXMakeCurrent(dpy, win, glc);

	glEnable(GL_TEXTURE_2D);
	glEnable(GL_DEPTH_TEST);

	InitGContext(&ctx, data, WIDTH * HEIGHT, WIDTH, HEIGHT, 0, 24);

	glGenTextures(1, &texture[0]);
	srand(time(NULL));

	while (1) {
		XNextEvent(dpy, &xev);

		if (xev.type == Expose || xev.type == ButtonPress) {
			XGetWindowAttributes(dpy, win, &gwa);
			glViewport(0, 0, gwa.width, gwa.height);
			DrawScene();
			glXSwapBuffers(dpy, win);
		} else if (xev.type == KeyPress) {
			break;
		}
	}

	glXMakeCurrent(dpy, None, NULL);
	glXDestroyContext(dpy, glc);
	XDestroyWindow(dpy, win);
	XCloseDisplay(dpy);

	return 0;
}
